/*
 * vectorizednet.c
 *
 *  Created on: Nov 19, 2013
 *      Author: cinus
 */

#include "vectorizednet.h"
#include "types.h"
#include "jjann.h"
#include "activation.h"
#include "training.h"

#include <stdlib.h>
#include <stdio.h>

afpv convertFunction(afp fptr);
afpv convertDiffFunction(afp fptr);
cdwv convertDeltaWeightFunction(cdw fptr);

vectorizedNet_tp convertNet(netdata_tp stdNet) {
	uint32 i, j, k;
	uint32 no, ni; //no = number of outputs, ni = number of inputs

	vectorizedNet_tp vNet;
	matrix_tp tmp;

	vNet = (vectorizedNet_tp) malloc(sizeof(vectorizedNet_t));
	vNet->numLayers = stdNet->layersize;
	vNet->neuronsInLayer = calloc(vNet->numLayers, sizeof(uint32));
	memcpy(vNet->neuronsInLayer, stdNet->neurons, vNet->numLayers * sizeof(uint32));
	vNet->w = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->dw = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->b = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->db = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->a = (matrix_tp*) calloc(sizeof(matrix_tp), vNet->numLayers);
	vNet->z = (matrix_tp*) calloc(sizeof(matrix_tp), vNet->numLayers);
	vNet->d = (matrix_tp*) calloc(sizeof(matrix_tp), vNet->numLayers);
	vNet->g = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->gb = (matrix_tp*) malloc(sizeof(matrix_tp) * vNet->numLayers - 1);
	vNet->af = calloc(vNet->numLayers, sizeof(afpv));
	vNet->daf = calloc(vNet->numLayers, sizeof(afpv));

	//copy of hte weights
	for (i = 0; i < stdNet->layersize; i++) {
		no = stdNet->neurons[i];
		ni = stdNet->neurons[i];
		if (i != stdNet->layersize - 1) {
			no = stdNet->neurons[i + 1];
			vNet->b[i] = newMatrix(no, 1);
			vNet->db[i] = newMatrix(no, 1);
			tmp = newMatrix(no, ni);
			//weights
			for (j = 0; j < stdNet->neurons[i]; j++) {
				setColumn(tmp, j, getNeuron(stdNet, i, j)->weights, 0);
			}
			//biases
			for (k = 0; k < no; k++) {
				set(vNet->b[i], k, 0, getNeuron(stdNet, i, j)->weights[k]);
			}

			vNet->w[i] = tmp;
			vNet->dw[i] = newMatrix(no, ni);
			vNet->g[i] = newMatrix(no, ni);
			vNet->gb[i] = newMatrix(no, 1);
			zeros(vNet->g[i]);
		}

		vNet->af[i] = convertFunction(getNeuron(stdNet, i, 0)->activationFunction);
		vNet->daf[i] = convertDiffFunction(getNeuron(stdNet, i, 0)->diffActivationFunction);

		vNet->a[i] = newMatrix(no, 1);
		vNet->z[i] = newMatrix(no, 1);
		vNet->d[i] = newMatrix(no, 1);

	}

	vNet->errorComputation = stdNet->errorComputation;
	vNet->onlineTraining = stdNet->settings.onlineTraining;
	vNet->computeDeltaWeights = convertDeltaWeightFunction(stdNet->computeDeltaWeights);
	vNet->alpha = stdNet->settings.alpha;
	vNet->eta = stdNet->settings.eta;
	vNet->maxEpochsFromMinimum = stdNet->settings.maxEpochsFromMinimum;
	vNet->maxEpochs = stdNet->settings.maxEpochs;
	return vNet;
}

uint32 neuronsInLayer(vectorizedNet_tp net, uint32 layerId) {
	/*if (layerId == net->numLayers - 1) {
	 return net->w[layerId-1]->cols - 1; //last layer has no bias
	 }
	 return net->w[layerId-1]->cols; //bias is also included*/
	return net->neuronsInLayer[layerId];
}

afpv convertFunction(afp fptr) {
	if (fptr == activationLinear) {
		return activationLinearV;
	} else if (fptr == activationSigmoid) {
		return activationSigmoidV;
	} else if (fptr == activationTanh) {
		return activationTanhV;
	} else if (fptr == activationSoftmax) {
		return activationSoftmaxV;
	} else {
		return NULL;
	}
}

afpv convertDiffFunction(afp fptr) {
	if (fptr == diffActivationLinear) {
		return diffActivationLinearV;
	} else if (fptr == diffActivationSigmoid) {
		return diffActivationSigmoidV;
	} else if (fptr == diffActivationTanh) {
		return diffActivationTanhV;
	} else if (fptr == diffActivationSoftmax) {
		return diffActivationSoftmaxV;
	} else {
		return NULL;
	}
}

cdwv convertDeltaWeightFunction(cdw fptr) {
	if (fptr == bpComputeDeltaWeights) {
		return bpComputeDeltaWeightsV;
	} else if (fptr == irpComputeDeltaWeights) {
		return irpComputeDeltaWeights;
	} else {
		return NULL;
	}
}

void printVecNet(vectorizedNet_tp vnet) {
	uint32 i;

	fprintf(stderr, "\t\tVectorized Net\n"
			"Layers: %d\nNeurons: [", vnet->numLayers);
	for (i = 0; i < vnet->numLayers; i++) {
		fprintf(stderr, "%d ", vnet->neuronsInLayer[i]);
	}
	fprintf(stderr, "]\nWeights\n"
			"");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->w[i]);
	}
	fprintf(stderr, "\nBiases\n");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->b[i]);
	}
	fprintf(stderr, "]\ndWeights\n"
			"");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->dw[i]);
	}
	fprintf(stderr, "\ndBiases\n");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->db[i]);
	}
	fprintf(stderr, "\nd\n");
	for (i = 0; i < vnet->numLayers; i++) {
		printMatrix(vnet->d[i]);
	}
	fprintf(stderr, "\na\n");
	for (i = 0; i < vnet->numLayers; i++) {
		printMatrix(vnet->a[i]);
	}
	fprintf(stderr, "\nz\n");
	for (i = 0; i < vnet->numLayers; i++) {
		printMatrix(vnet->z[i]);
	}
	fprintf(stderr, "\ng\n");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->g[i]);
	}
	fprintf(stderr, "\ngb\n");
	for (i = 0; i < vnet->numLayers - 1; i++) {
		printMatrix(vnet->gb[i]);
	}
}

void reserveNetSpaceV(vectorizedNet_tp srcVNet, vectorizedNet_tp dstVNetPtr) {
	dstVNetPtr->neuronsInLayer = calloc(srcVNet->numLayers, sizeof(uint32));
	dstVNetPtr->w = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->dw = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->b = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->db = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->a = (matrix_tp*) calloc(sizeof(matrix_tp), srcVNet->numLayers);
	dstVNetPtr->z = (matrix_tp*) calloc(sizeof(matrix_tp), srcVNet->numLayers);
	dstVNetPtr->d = (matrix_tp*) calloc(sizeof(matrix_tp), srcVNet->numLayers);
	dstVNetPtr->g = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->gb = (matrix_tp*) malloc(sizeof(matrix_tp) * srcVNet->numLayers - 1);
	dstVNetPtr->af = calloc(srcVNet->numLayers, sizeof(afpv));
	dstVNetPtr->daf = calloc(srcVNet->numLayers, sizeof(afpv));
}

void copyNetV(vectorizedNet_tp srcVNet, vectorizedNet_tp dstVNet) {
	uint32 i;

	dstVNet->alpha = srcVNet->alpha;
	dstVNet->computeDeltaWeights = srcVNet->computeDeltaWeights;
	dstVNet->error = srcVNet->error;
	dstVNet->errorComputation = srcVNet->errorComputation;
	dstVNet->eta = srcVNet->eta;
	dstVNet->lastError = srcVNet->lastError;
	dstVNet->numLayers = srcVNet->numLayers;
	dstVNet->onlineTraining = srcVNet->onlineTraining;
	dstVNet->maxEpochs = srcVNet->maxEpochs;
	dstVNet->maxEpochsFromMinimum = srcVNet->maxEpochsFromMinimum;
	memcpy(dstVNet->neuronsInLayer, srcVNet->neuronsInLayer, sizeof(uint32) * srcVNet->numLayers);
	for (i = 0; i < dstVNet->numLayers - 1; i++) {
		dstVNet->b[i] = copyMatrix(srcVNet->b[i]);
		dstVNet->db[i] = copyMatrix(srcVNet->db[i]);
		dstVNet->w[i] = copyMatrix(srcVNet->w[i]);
		dstVNet->dw[i] = copyMatrix(srcVNet->dw[i]);
		dstVNet->g[i] = copyMatrix(srcVNet->g[i]);
		dstVNet->gb[i] = copyMatrix(srcVNet->gb[i]);

		dstVNet->af[i] = srcVNet->af[i];
		dstVNet->daf[i] = srcVNet->daf[i];

		dstVNet->a[i] = copyMatrix(srcVNet->a[i]);
		dstVNet->z[i] = copyMatrix(srcVNet->z[i]);
		dstVNet->d[i] = copyMatrix(srcVNet->d[i]);
	}
	dstVNet->af[i] = srcVNet->af[i];
	dstVNet->daf[i] = srcVNet->daf[i];

	dstVNet->a[i] = copyMatrix(srcVNet->a[i]);
	dstVNet->z[i] = copyMatrix(srcVNet->z[i]);
	dstVNet->d[i] = copyMatrix(srcVNet->d[i]);

}

void destroyNetworkV(vectorizedNet_tp vnet) {
	uint32 i;

	free(vnet->neuronsInLayer);

	for (i = 0; i < vnet->numLayers - 1; i++) {
		freeData(vnet->w[i]);
		free(vnet->w[i]);
		freeData(vnet->dw[i]);
		free(vnet->dw[i]);
		freeData(vnet->b[i]);
		free(vnet->b[i]);
		freeData(vnet->db[i]);
		free(vnet->db[i]);
		freeData(vnet->a[i]);
		free(vnet->a[i]);
		freeData(vnet->z[i]);
		free(vnet->z[i]);
		freeData(vnet->d[i]);
		free(vnet->d[i]);
		freeData(vnet->g[i]);
		free(vnet->g[i]);
		freeData(vnet->gb[i]);
		free(vnet->gb[i]);
	}
	freeData(vnet->a[i]);
	free(vnet->a[i]);
	freeData(vnet->z[i]);
	free(vnet->z[i]);
	freeData(vnet->d[i]);
	free(vnet->d[i]);

	free(vnet->w);
	free(vnet->dw);
	free(vnet->b);
	free(vnet->db);
	free(vnet->a);
	free(vnet->z);
	free(vnet->d);
	free(vnet->g);
	free(vnet->gb);
	free(vnet->af);
	free(vnet->daf);
	free(vnet);
}
